﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Subtle.Data.Persistence.Interfaces;
using Subtle.Domain.Services.Interfaces;
using Subtle.Domain.Models;
using Subtle.Domain.Services;

namespace Subtle.Test
{
	/// <summary>
	/// User Tests
	/// </summary>
	[TestClass]
	public class UserTests
	{
		private Mock<IRepository> userUnitOfWorkMock = new Mock<IRepository>();
		private Mock<IEmailService> emailServiceMock = new Mock<IEmailService>();
		private Mock<IUserService> userServiceMock = new Mock<IUserService>();
		private IUserService userService;

		/// <summary>
		/// Initialize a new User Unit Test
		/// </summary>
		public UserTests()
		{
			userService = new UserService(userUnitOfWorkMock.Object, emailServiceMock.Object);
		}

		/// <summary>
		/// Create User With Valid Credentials
		/// </summary>
		[TestMethod]
		public void CreateUserWithValidCredentials()
		{
			User user = new User
			{
				Username = "test@test.com",
				Password = "testing123"
			};
		
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);
			emailServiceMock.Setup(service => service.Send(String.Empty, String.Empty, String.Empty, String.Empty)).Returns(true);

			Assert.IsTrue(ValidationHelper.ValidateModel(user).Count == 0);
			Assert.IsTrue(userService.CreateNonValidatedUser(user) == UserCreateStatus.Success);
		}

		/// <summary>
		/// Create User With Invalid Password
		/// </summary>
		[TestMethod]
		public void CreateUserWithInvalidPassword()
		{
			User user = new User
			{
				Username = "test@test.com",
				Password = String.Empty
			};

			var userUnitOfWorkMock = new Mock<IRepository>();
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsFalse(ValidationHelper.ValidateModel(user).Count == 0);
		}

		/// <summary>
		/// Create User With Invalid Password
		/// </summary>
		[TestMethod]
		public void CreateUserWithInvalidPasswordWithLengthMoreThan10()
		{
			User user = new User
			{
				Username = "test@test.com",
				Password = "1234567891011"
			};

			var userUnitOfWorkMock = new Mock<IRepository>();
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsFalse(ValidationHelper.ValidateModel(user).Count == 0);
		}

		/// <summary>
		/// Create User With Invalid Password
		/// </summary>
		[TestMethod]
		public void CreateUserWithValidPasswordWithLengthOf6()
		{
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456"
			};

			var userUnitOfWorkMock = new Mock<IRepository>();
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsTrue(ValidationHelper.ValidateModel(user).Count == 0);
		}

		/// <summary>
		/// Validate User with Valid Verification Guid
		/// </summary>
		[TestMethod]
		public void ValidateUserWithValidVerificationGuid()
		{
			Guid validGuid = Guid.NewGuid();
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456",
				VerificationGuid = validGuid.ToString()
			};

			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsTrue(userService.ValidateUser(user, validGuid.ToString()));
		}

		/// <summary>
		/// Validate User with Invalid Verification Guid
		/// </summary>
		[TestMethod]
		public void ValidateUserWithInvalidVerificationGuid()
		{
			Guid validGuid = Guid.NewGuid();
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456",
				VerificationGuid = Guid.NewGuid().ToString()
			};

			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsFalse(userService.ValidateUser(user, validGuid.ToString()));
		}

		/// <summary>
		/// Change a User's Password to a Valid New Password
		/// </summary>
		[TestMethod]
		public void ChangeToValidPassword()
		{
			string newPassword = "654321";
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456"
			};
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsTrue(ValidationHelper.ValidateModel(user).Count == 0);
			Assert.IsTrue(userService.ChangePassword(user, newPassword));
			Assert.IsTrue(user.Password == newPassword);
		}

		/// <summary>
		/// Change a User's Password to an Invalid Password
		/// </summary>
		[TestMethod]
		public void ChangeToInvalidPassword()
		{
			string newPassword = "6543212312311";
			User user = new User
			{
				Username = "test@test.com",
				Password = newPassword
			};
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);

			Assert.IsFalse(ValidationHelper.ValidateModel(user).Count == 0);
		}

		/// <summary>
		/// Change a User's Email Address to a Valid New Email Address
		/// </summary>
		[TestMethod]
		public void ChangeToValidEmailAddress()
		{
			string newEmailAddress = "pieter@pieterg.com";
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456"
			};
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);
			userServiceMock.Setup(service => service.GetUser(newEmailAddress)).Returns<User>(null);

			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Query<User>()).Returns(GetUser(new List<User> { user }));

			Assert.IsTrue(ValidationHelper.ValidateModel(user).Count == 0);
			Assert.IsTrue(userService.ChangeEmailAddress(user, newEmailAddress));
			Assert.IsTrue(user.Username == newEmailAddress);
		}

		/// <summary>
		/// Change a User's Email Address to an Invalid New Email Address
		/// </summary>
		[TestMethod]
		public void ChangeToValidEmailAddressButDuplicateExists()
		{
			string newEmailAddress = "pieter@pieterg.com";
			User user = new User
			{
				Username = "test@test.com",
				Password = "123456"
			};
			User otherUser = new User
			{
				Username = "pieter@pieterg.com",

			};
			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Save(user)).Returns(true);
			userServiceMock.Setup(service => service.GetUser(newEmailAddress)).Returns<User>(null);

			userUnitOfWorkMock.Setup(unitOfWork => unitOfWork.Query<User>()).Returns(GetUser(new List<User>{user, otherUser}));

			Assert.IsTrue(ValidationHelper.ValidateModel(user).Count == 0);
			Assert.IsFalse(userService.ChangeEmailAddress(user, newEmailAddress));
			Assert.IsFalse(user.Username == newEmailAddress);
		}

		/// <summary>
		/// Get User Helper method for mocking
		/// </summary>
		/// <param name="user"></param>
		/// <returns></returns>
		private IQueryable<User> GetUser(List<User> users)
		{
			return users.AsQueryable();
		}
	}
}
